#!/usr/bin/env python
# -*- coding: utf-8 -*-

from odoo import models, fields, api, _, tools
from markupsafe import Markup
from odoo.exceptions import UserError, ValidationError

class QualityCheck(models.Model):
    _inherit = 'quality.check'

    #  return_picking_id = fields.Many2one('stock.picking')
    check_to_stock_picking_id = fields.Many2one('stock.picking')
    sb_workflow_template_id = fields.Many2one('sb.workflow.template', string="Approval Workflow Template",
                                              domain="[('sb_workflow_template_item_ids', '!=', False),"
                                                     "('workflow_type', 'in', ['quality_check'])]")
    sb_workflow_id = fields.Many2one('sb.workflow', string="Current Approval Workflow", copy=False)
    sb_workflow_state = fields.Selection(related="sb_workflow_id.state",
                                         readonly=True,
                                         string="Approval Status")
    sb_workflow_item_ids = fields.One2many(related="sb_workflow_id.sb_workflow_item_ids", string="Approval Items")
    sb_workflow_attachments_ids = fields.One2many(related="sb_workflow_id.sb_workflow_attachments_ids",
                                                  string="Approval Attachments")
    sb_workflow_ids = fields.One2many('sb.workflow', 'quality_check_id', string="Approval Workflows")
    sb_workflow_ids_sum = fields.Integer(compute="_compute_sb_workflow_ids_sum", store=True)
    reject_reason_ids = fields.One2many(
        'quality.reject.reason', 
        'check_id',
        string='Reject Reasons'
    )
    purchase_line_id = fields.Many2one(
        'purchase.order.line',
        string="采购订单行",
        compute='_compute_purchase_line_id',
        store=True
    )
    product_uom_qty = fields.Float(
        '数量',
        digits='Purchase Qty',
        compute='_compute_qty',
        store=True
    )
    incoming_qty = fields.Float(
        '入库数量',
        digits='Product Unit of Measure',
        compute='_compute_incoming_qty',
        store=False
    )
    product_uom = fields.Many2one(
        'uom.uom',
        string='产品单位',
        related='product_id.uom_id',
        store=True
    )
    rejected_qty = fields.Float('拒收数量', digits='Product Unit of Measure', compute='_compute_incoming_qty')
    refresh_qty = fields.Boolean('Refresh Qty')
    s_confirm_time = fields.Datetime('Workflow confirm time', related="sb_workflow_id.approval_time")
    s_wf_wait_hours = fields.Integer('Workflow waiting hours', default="4")
    s_approval_last_time = fields.Datetime('Workflow Approval last time', compute="_get_wf_approval_last_time")
    s_wf_last_warn_datetime = fields.Datetime('Workflow last warning datetime', readonly=True)
    s_warn_qty = fields.Integer('Warn QTY', readonly=True)
    s_wf_warn_datetime = fields.Datetime('Workflow warning datetime', compute="_get_wf_warn_time")
    s_is_timeout = fields.Boolean('Workflow Timeout', compute="_get_wf_warn_time")

    def check_and_send_quality_noti_message(self):
        timeout_check_rows = self.search([('sb_workflow_state','=','approval')]).filtered(lambda x:x.s_is_timeout)
        timeout_check_rows.send_notification()

    @api.depends('sb_workflow_item_ids.approval_time', 's_confirm_time')
    def _get_wf_approval_last_time(self):
        for row in self:
            if not row.sb_workflow_id:
                row.s_approval_last_time = row.create_date
                continue
            pass_wf_items = row.sb_workflow_item_ids.filtered(lambda x:x.state in ('pass','no_pass'))
            if pass_wf_items:
                row.s_approval_last_time = max(pass_wf_items.mapped('approval_time'))
            else:
                row.s_approval_last_time = row.s_confirm_time
    
    @api.depends('s_approval_last_time', 's_wf_wait_hours', 's_wf_warn_datetime')
    def _get_wf_warn_time(self):
        from dateutil.relativedelta import relativedelta
        for row in self:
            if not row.sb_workflow_id or row.s_wf_wait_hours < 1:
                #  row.s_wf_warn_datetime = row.s_approval_last_time + relativedelta(hours=row.s_wf_wait_hours)
                row.s_wf_warn_datetime = False
                row.s_is_timeout = False
            elif row.s_wf_last_warn_datetime:
                row.s_wf_warn_datetime = row.s_wf_last_warn_datetime + relativedelta(hours=row.s_wf_wait_hours)
                row.s_is_timeout = row.s_wf_warn_datetime <= fields.Datetime.now()
            else:
                row.s_wf_warn_datetime = row.s_approval_last_time + relativedelta(hours=row.s_wf_wait_hours)
                row.s_is_timeout = row.s_wf_warn_datetime <= fields.Datetime.now()

    def split_no_quality_check_stock_move(self):
        self.ensure_one()
        if self.env.ref('sb_iqc.iqc_inspection_point') and self.point_id != self.env.ref('sb_iqc.iqc_inspection_point'):
            return False
        if self.picking_id and self.picking_id.move_ids.filtered(lambda x:not x.product_id.requires_iqc and x.state != 'cancel') \
                and self.picking_id.move_ids.filtered(lambda x:x.product_id.requires_iqc and x.state != 'cancel'):
            row = self.picking_id
            # 创建新调拨单（欠单）
            backorder = row.copy({
                'name': row.name + ' - 非质检产品',
                'move_ids': [],  # 清空复制的移动
                'backorder_id': row.id,  # 关联原单
            })
            for move in row.move_ids.filtered(lambda x: not x.product_id.requires_iqc and x.state != 'cancel'):
                # 将非质检移动转移到新单
                move.copy({
                    'picking_id': backorder.id,
                    'state': 'draft',  # 重置状态
                    'move_orig_ids': [(6, 0, [])],  # 清除原始关联
                })
                # 原移动取消
                move._action_cancel()
                move.unlink()
                
            # 自动确认新调拨单
            backorder.action_confirm()
            #  backorder.button_validate()
            if self.env.ref('sb_iqc.stock_location_inspection'):
                row.location_dest_id = self.env.ref('sb_iqc.stock_location_inspection').id
                row.name = row.name + ' - 质检产品'
        return True

    def create_iqc_quality_to_stock(self):
        self.ensure_one()
        """创建从待检库位到目标库位的调拨"""
        if self.env.ref('sb_iqc.iqc_inspection_point') and self.point_id != self.env.ref('sb_iqc.iqc_inspection_point'):
            return False
        if self.filtered(lambda x:x.quality_state != 'pass'):
            raise UserError(_('Find unconfirm quality check, {product} state is : {state}').format(
                product=self.product_id.name,
                state = self.quality_state))
        uncheck_quality_rows = self.picking_id.check_ids.filtered(lambda x:x.quality_state == 'none')
        if uncheck_quality_rows:
            raise UserError(_('Find unconfirm quality check: {check_text}').format(
                check_text = '/'.join(uncheck_quality_rows.mapped(lambda x:'( '+x.product_id.name+' : '+x.quality_state + ' )'))))
        if not self.check_to_stock_picking_id and self.picking_id and self.picking_id.location_dest_id == self.env.ref('sb_iqc.stock_location_inspection'):
            if self.picking_id.check_ids.filtered(lambda x:x.check_to_stock_picking_id) and self in self.picking_id.check_ids.filtered(lambda x:not x.check_to_stock_picking_id):
                check_to_stock_picking_id = self.picking_id.check_ids.filtered(lambda x:x.check_to_stock_picking_id).check_to_stock_picking_id
                no_to_stock_ref_rows = self.picking_id.check_ids.filtered(lambda x:not x.check_to_stock_picking_id)
                no_to_stock_ref_rows.update({'check_to_stock_picking_id':check_to_stock_picking_id.id})
                return True
            move_lines = []
            for y in self.picking_id.move_ids:
                move_vals = {
                    'product_id': y.product_id.id,
                    'name': y.product_id.name,
                    'product_uom_qty': y.quantity,
                    'quantity': y.quantity,
                    'product_uom': y.product_uom.id,
                    'location_id': self.env.ref('sb_iqc.stock_location_inspection').id,
                    'location_dest_id': self.env.ref('stock.stock_location_stock').id,
                    'picking_type_id': self.env.ref('stock.picking_type_internal').id,
                    'lot_ids': [(6, 0, y.lot_ids.ids)],
                    'move_line_ids': [(0, 0, {
                        'product_id': x.product_id.id,
                        'lot_id': x.lot_id.id,
                        'quantity': x.quantity,
                        'location_id': self.env.ref('sb_iqc.stock_location_inspection').id,
                        'location_dest_id': self.env.ref('stock.stock_location_stock').id,
                    }) for x in y.move_line_ids]
                }
                move_lines.append(move_vals)
            picking = self.env['stock.picking'].create({
                'location_id': self.env.ref('sb_iqc.stock_location_inspection').id,
                'location_dest_id': self.env.ref('stock.stock_location_stock').id,
                'picking_type_id': self.env.ref('stock.picking_type_internal').id,
                'move_ids': [(0, 0, r) for r in move_lines],
            })
            picking.button_validate()
            self.update({'check_to_stock_picking_id':picking.id})
            if self.picking_id.check_ids.filtered(lambda x:x.check_to_stock_picking_id) and self not in self.picking_id.check_ids.filtered(lambda x:not x.check_to_stock_picking_id):
                check_to_stock_picking_id = self.picking_id.check_ids.filtered(lambda x:x.check_to_stock_picking_id).check_to_stock_picking_id
                no_to_stock_ref_rows = self.picking_id.check_ids.filtered(lambda x:not x.check_to_stock_picking_id)
                no_to_stock_ref_rows.update({'check_to_stock_picking_id':check_to_stock_picking_id.id})
        return True

    @api.model_create_multi
    def create(self, vals_list):
        res = super().create(vals_list)
        if res and res.point_id == self.env.ref('sb_iqc.iqc_inspection_point'):
            move_lines = res.picking_id.move_line_ids.filtered(lambda x:x.product_id == res.product_id)
            if len(move_lines) == 1:
                res.lot_name = move_lines.lot_name
            res.send_notification()
        return res

    def send_notification(self):
        for row in self.filtered(lambda x:x.quality_state == 'none'):
            row._send_notification()
            row.s_warn_qty += 1
            row.s_wf_last_warn_datetime = fields.Datetime.now()
            if row.s_warn_qty == 3 and row.s_wf_wait_hours > 2:
                row.s_wf_wait_hours //= 2

    def get_base_url(self):
        base_url = self.env['ir.config_parameter'].sudo().get_param(
            'web.base.url', 
        )
        # 清理URL末尾的斜杠
        return base_url.rstrip('/')

    def get_full_url(self):
        base_url = self.get_base_url()
        menu_id = self.env.ref('sb_iqc.menu_quality_checks_iqc')
        action_id = self.env.ref('sb_iqc.action_quality_check_view_iqc')
        url = f'{base_url}/web#id={self.id}&model=quality.check&view_type=form'
        iqc_active_id = self.env.ref('sb_iqc.quality_alert_iqc_team0')
        url = url + f'&menu_id={menu_id.id}' if menu_id else '' 
        url = url + f'&action={action_id.id}' if action_id else ''
        url = url + f'&active_id={iqc_active_id.id}' if iqc_active_id else ''
        return url

    def _generate_quality_check_link(self):
        """生成质检记录的可点击链接"""
        url = self.get_full_url()
        return f'''
        <a href="{url}">
            质检单 {self.name}
        </a>
        '''

    def _send_notification(self):
        self.ensure_one()
        message_body = f'''
        <p>新的质检单已创建，请及时处理：{self._generate_quality_check_link()}</p>
        <ul>
            <li>产品：{self.product_id.name}</li>
            <li>批次：{self.lot_name or '无'}</li>
            <li>数量：{self.product_uom_qty}</li>
            <li>单位：{self.product_uom.name}</li>
        </ul>
        '''
        message_body = tools.html_sanitize(
            message_body,
            sanitize_attributes=False,  # 允许style属性
            sanitize_style=True         # 启用样式安全检查（可选）
        )
        #  msg = f"新质检单已生成：{self.name}，产品：{self.product_id.name}"
        noti_group = self.env.ref('sb_iqc.iqc_check_notification_group')
        partner_ids = noti_group.users.mapped(lambda x:x.partner_id).ids if noti_group else [] 
        emails = noti_group.users.mapped('email') if noti_group else []
        valid_emails = [email for email in emails if email]
        self.message_post(
            message_type='comment', 
            body=message_body,
            partner_ids=partner_ids,
            subtype_xmlid='sb_iqc.mt_iqc_internal_alert',
            #  subtype_xmlid='mail.mt_comment',   # 使用内部评论子类型
            #  email_from=False,  # 阻止邮件生成
        )
        # 发送邮件
        #  template = self.env.ref('sb_iqc.email_template_iqc')
        #  template.send_mail(self.id, force_send=False)

    def action_selection_reject(self):
        self.ensure_one()
        #  if self.quality_state == 'none' and self.sb_workflow_state == 'approve' and self.rejected_qty > 0:
            #  self._adjust_receipt_qty()
        self.do_pass()

    def action_entire_reject(self):
        self.ensure_one()
        #  if self.quality_state == 'none' and self.sb_workflow_state == 'approve' and self.rejected_qty > 0:
            #  self._adjust_receipt_qty(entire_reject=True)
        self.do_fail()

    @api.depends('sb_workflow_ids')
    def _compute_sb_workflow_ids_sum(self):
        for item in self:
            if item.sb_workflow_ids:
                item.sb_workflow_ids_sum = len(item.sb_workflow_ids)
            else:
                item.sb_workflow_ids_sum = 0

    def action_view_workflow(self):
        return {
            'name': 'Approval Flow',
            'type': 'ir.actions.act_window',
            'res_model': 'sb.workflow',
            'view_mode': 'tree',
            'target': 'current',
            'domain': [('quality_order_id', '=', self.id)],
        }

    def action_workflow(self):
        return {
            'name': 'Approval Flow',
            'type': 'ir.actions.act_window',
            'res_model': 'sb.workflow',
            'view_mode': 'form',
            'res_id': self.sb_workflow_id.id,
            # 'views': [(self.env.ref('').id, 'form')],
            'target': 'current',
        }

    @api.depends('reject_reason_ids', 'refresh_qty')
    def _compute_incoming_qty(self):
        for record in self:
            related_move = sum(record.reject_reason_ids.mapped(lambda x:x.quantity)) if record.reject_reason_ids else 0
            record.rejected_qty = related_move
            record.incoming_qty = record.product_uom_qty - related_move

    @api.depends('picking_id.move_ids', 'refresh_qty')
    def _compute_qty(self):
        for record in self:
            related_move = record.picking_id.move_ids.filtered(lambda x:x.product_id == record.product_id).filtered(
                lambda m: m.product_uom_qty
            )[:1]
            record.product_uom_qty = related_move.product_uom_qty if related_move else False

    @api.depends('picking_id.move_ids')
    def _compute_purchase_line_id(self):
        for record in self:
            related_move = record.picking_id.move_ids.filtered(lambda x:x.product_id == record.product_id).filtered(
                lambda m: m.purchase_line_id
            )[:1]
            record.purchase_line_id = related_move.purchase_line_id if related_move else False

    # 生成审批流
    def create_workflow(self):
        if self.sb_workflow_template_id:
            if len(self.sb_workflow_ids) > 0:
                workflow_sum = len(self.sb_workflow_ids) + 1
            else:
                workflow_sum = 1
            # 创建审批流
            new_wf_id = self.env['sb.workflow'].sudo().create({
                #  'name': self.name + '- Approval Flow' + '-' + str(workflow_sum),
                'name': self.name,
                'approval_time': fields.Datetime.now(),
                'workflow_type': 'quality_check',
                'quality_check_id': self.id,
            })
            self.sb_workflow_id = new_wf_id.id
            # 根据模板生成审批项
            item_audit_levels = []
            for item in self.sb_workflow_template_id.sb_workflow_template_item_ids:
                new_item = self.env['sb.workflow.item'].sudo().create({
                    'name': item.name,
                    'user_group_ids': item.user_group_ids.ids,
                    'audit_level': item.audit_level,
                    'sb_workflow_id': new_wf_id.id,
                    'state': 'wait_forward'
                })
                # 收集生成的审核项的所有层级
                item_audit_levels.append(new_item.audit_level)
            # 去重排序
            item_audit_levels = list(set(item_audit_levels))
            item_audit_levels.sort()
            # 开放最低层级的神匹配
            for n_item in new_wf_id.sb_workflow_item_ids:
                if n_item.audit_level == item_audit_levels[0]:
                    n_item.state = 'approval'
        else:
            raise ValidationError('Please select an approval template.')

    #  @api.model_create_multi
    #  def create(self, values):
      #  res = super(QualityCheck, self).create(values)
      #  return res

    #  def write(self, vals):
        #  if vals.get('reject_reason_ids') and self.filtered(lambda x:x.sb_workflow_id):
            #  raise ValidationError('审批状态错误，不能改变数量')
        #  elif vals.get('reject_reason_ids') and self.filtered(lambda x:x.state != 'none'):
            #  raise ValidationError('状态错误，不能改变数量')
        #  res = super(QualityCheck, self).write(vals)
        #  return res

    #@api.model
    #def default_get(self, fields_list):
    #    res = super(QualityCheck, self).default_get(fields_list)
    #    # 从上下文中获取关联的调拨单ID
    #    picking_id = self.env.context.get('default_picking_id')
    #    if not picking_id:
    #        return res
    #    # 查找原调拨单对应的退货调拨单
    #    picking = self.env['stock.picking'].browse(picking_id)
    #    # 根据业务逻辑调整搜索条件
    #    return_picking = self.env['stock.picking'].search([
    #        ('origin', '=', picking.name),
    #        ('picking_type_id.code', '=', 'incoming')  # 示例条件，需确认实际类型
    #    ], order='create_date DESC', limit=1)
    #    if return_picking:
    #        res['return_picking_id'] = return_picking.id
    #    return res

    def do_fail(self):
        self.ensure_one()
        if self.reject_reason_ids and self.point_id == self.env.ref('sb_iqc.iqc_inspection_point'):
            if not self.sb_workflow_id:
                raise ValidationError(_('The order has not been approved. Please submit for approval first.'))
            if self.sb_workflow_state != 'approve':
                raise ValidationError(_('Quality Check Approval state is not Passed！'))
        if self.point_id == self.env.ref('sb_iqc.iqc_inspection_point'):
            self._adjust_receipt_qty(entire_reject=True)
        return super(QualityCheck, self).do_fail()

    def do_pass(self):
        self.ensure_one()
        if self.reject_reason_ids and self.point_id == self.env.ref('sb_iqc.iqc_inspection_point'):
            if not self.sb_workflow_id:
                raise ValidationError(_('The order has not been approved. Please submit for approval first.'))
            if self.sb_workflow_state != 'approve':
                raise ValidationError(_('Quality Check Approval state is not Passed！'))
            self._adjust_receipt_qty()
        res = super(QualityCheck, self).do_pass()
        return res

    #def create_return_picking(self):
    #    self.ensure_one()
    #    return_picking = self.env['stock.return.picking'].create({
    #        'picking_id': self.picking_id.id
    #    })._create_returns()
    #    
    #    picking = self.env['stock.picking'].browse(return_picking['res_id'])
    #    move = picking.move_lines.filtered(
    #        lambda m: m.product_id == self.product_id)
    #    
    #    move.write({
    #        'product_uom_qty': self.rejected_qty,
    #        'quality_check_id': self.id
    #    })
    #    
    #    self.write({
    #        'return_move_id': move.id
    #    })
    #    
    #    return {
    #        'name': 'Return Picking',
    #        'type': 'ir.actions.act_window',
    #        'view_mode': 'form',
    #        'res_model': 'stock.picking',
    #        'res_id': picking.id
    #    }

    def _adjust_receipt_qty(self, entire_reject=False):
        """调整收货单数量"""
        self.ensure_one()
        origin_move = self.picking_id.move_ids.filtered(
            lambda m: m.product_id == self.product_id
        )
        if origin_move:
            origin_move.write({
                #  'product_uom_qty': origin_move.product_uom_qty - self.rejected_qty,
                'rejected_qty': self.rejected_qty if not entire_reject else origin_move.product_uom_qty,
                'quantity': origin_move.product_uom_qty - self.rejected_qty if not entire_reject else 0
            })
        else:
            raise UserError("未找到对应的收货单移动！")


class QualityPoint(models.Model):
    _inherit = "quality.point"

    product_domain = fields.Char('Product Domain', default="")

    def _get_checks_values(self, products, company_id, existing_checks=False):
        point_values = super(QualityPoint, self)._get_checks_values(products=products, company_id=company_id, existing_checks=existing_checks)
        return [x for x in point_values if self.env['product.product'].search([('id','=',x.get('product_id'))]).requires_iqc]

class QualityAlertTeam(models.Model):
    _inherit = "quality.alert.team"

    iqc_check_count = fields.Integer('IQC Quality Checks', compute='_compute_iqc_check_count')
    #  color = fields.Integer('Color', default=1)

    def _compute_iqc_check_count(self):
        iqc_team_id = self.env.ref('sb_iqc.quality_alert_iqc_team0')
        check_data = self.env['quality.check']._read_group([('team_id', 'in', iqc_team_id.ids), ('quality_state', '=', 'none')], ['team_id'], ['__count'])
        check_result = {team.id: count for team, count in check_data}
        for team in self:
            team.iqc_check_count = check_result.get(team.id, 0)
